#coding=utf8
#-------------------------------------------------------------------------------
# Name:        ??1
# Purpose:
#
# Author:      weigun
#
# Created:     11-08-2011
# Copyright:   (c) weigun 2011
# Licence:     <your licence>
#-------------------------------------------------------------------------------
#!/usr/bin/env python
import re,os,sys
import time
import cPickle
import hashlib
import itertools
import requests
import json

from pypinyin import lazy_pinyin
from Account import account
from cmdArgs import options
from functools import wraps
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.select import Select
from selenium.webdriver.common.keys import Keys
from selenium.common.exceptions import NoSuchElementException,TimeoutException
from selenium.webdriver.remote.webelement import WebElement

from dataSet import DataSet
import pickledb

driver_list = {
    'Chrome' : webdriver.Chrome,
    'PhantomJS' : webdriver.PhantomJS,
    'Ie' : webdriver.Ie,
    'Firefox' : webdriver.Firefox,
}

TIME_OUT = 30

DATA_FILE = 'db.dat'

G_DIFF_TIMES = {} #--bugid : diff_times

APPID = 0

def load():
    if os.path.exists(DATA_FILE):
        with open(DATA_FILE) as fh:
            return cPickle.load(fh)
    return {}

def save(data):
    with open(DATA_FILE,'w') as fh:
        cPickle.dump(data,fh)

def to_number(val):
    return int(val.replace(',',''))

class Tools(object):
    """
        class comment
    """
    fh = open('crash_devices.log','w')

    @classmethod
    def save(cls,name,times):
        try:
            line = u'\t'.join([name,times,u"\n"])
            cls.fh.write(line)
        except:
            pass

    @classmethod
    def close(cls):
        cls.fh.close()

class Log(object):
    """
        class comment
    """
    fh = open('exection.log','w')

    @classmethod
    def save(cls,line):
        cls.fh.write(line + "\n")

    @classmethod
    def close(cls):
        cls.fh.close()

class SingletonMetaclass(type):
    """Singleton Metaclass"""

    def __init__(self, name, bases, dic):
        super(SingletonMetaclass, self).__init__(name, bases, dic)
        self.instance = None

    def __call__(self, *args, **kwargs):
        if self.instance is None:
            self.instance = super(SingletonMetaclass, self).__call__(*args, **kwargs)
        return self.instance


class WindowsManager(object):
    """
        class comment
    """
    __metaclass__ = SingletonMetaclass

    def __init__(self):
        self.win_tab = {}
        self.driver = Driver.get_driver()

    def get_pages_by_win(self,hwd):
        return self.win_tab[hwd]

    def each_win(self):
        return ( hwd for hwd in self.win_tab)

    def bind(self,hwd,page_obj):
        print hwd,page_obj
        if self.win_tab.has_key(hwd):
            self.win_tab[hwd].append(page_obj)
        else:
            self.win_tab.setdefault(hwd,[])
            self.win_tab[hwd].append(page_obj)

    def remove_page(self,hwd,page_obj):
        print 'remove:',page_obj
        try:
            self.win_tab[hwd].remove(page_obj)
        except ValueError:
            print 'remove page error,hwd and pageobj not match',hwd,page_obj,' this info is harmless'
            return False
        if len(self.win_tab[hwd]) <= 0:
            if len(self.win_tab) - 1 > 0:
                self.win_tab.pop(hwd)
                print 'close window:',hwd,self.driver.title
                self.driver.close()
                Driver.switch_win(self.win_tab.keys()[0])
        return True

    def unbind(self,page_obj,which_win = None):
        if not which_win:
            return False
        ret = self.remove_page(which_win,page_obj)
        if ret:
            return ret
        for hwd in self.each_win():
            if self.remove_page(which_win,page_obj):
                return True
        return False

class Driver(object):
    """
        class comment
    """
    driver_instance = None

    def __init__(self):
        pass

    @classmethod
    def get_driver(cls,drv_type = 'Chrome'): #--Chrome PhantomJS
        if not cls.driver_instance:
            print 'init driver'
            driver = driver_list[drv_type]()
            driver.implicitly_wait(TIME_OUT)
            cls.driver_instance = driver
            cls.get_driver = cls.__has_get_driver
        print 'just call this method once!!'
        return cls.driver_instance

    @classmethod
    def __has_get_driver(cls):
        return cls.driver_instance

    @classmethod
    def get_cur_url(cls):
        WaitFor.until(lambda:cls.driver_instance.current_url is not None and cls.driver_instance.current_url != '')
        return cls.driver_instance.current_url

    @classmethod
    def switch_win(cls,hwd):
        print 'switch to ',hwd
        cls.driver_instance.switch_to_window(hwd)

    @classmethod
    def get_all_wins(cls):
        time.sleep(1)   #--避免又是获取不全，加了1秒的延迟
        return cls.driver_instance.window_handles

    @classmethod
    def get_title(cls):
        WaitFor.until(lambda: cls.driver_instance.title is not None and cls.driver_instance.title != '')
        return cls.driver_instance.title

    @classmethod
    def reload(cls):
        cls.driver_instance.refresh()

class ExceptionHandler(object):
    """
        class comment
    """
    @staticmethod
    def timeout_handler(func):
        @wraps(func)
        def _handler(cls,key,param = None,parent = None,cb = None,*args,**kwargs):
            #print func,cls,key,cb,args,kwargs
            try:
                ret = func(cls,key,param,parent)
                if ret:
                    return ret
            except TimeoutException:
                if cb is not None and callable(cb):
                    print 'WaitFor:TimeoutException,callback start:',cb.__name__,args,kwargs
                    return cb(*args,**kwargs)
                else:
                    print 'TimeoutException occur with ',func,cls,key,param,parent,cb,args,kwargs
        return _handler

    @staticmethod
    def no_element_handler(func):
        @wraps(func)
        def _handler(cls,key,cb = None,*args,**kwargs):
            #print cls,key,cb,args,kwargs
            try:
                ret = func(cls,key)
                if ret:
                    return True
            except NoSuchElementException:
                if cb is not None:
                    print 'NoSuchElementException,callback start:',cb.__name__,args,kwargs
                    return cb(*args,**kwargs)
        return _handler


class FindElementBy(object):
    """
        class comment
        FindElementBy.class_name('login')
    """
    drv = Driver.get_driver()
    waiter = WebDriverWait(drv, TIME_OUT)

    def __init__(self):
        pass

    @classmethod
    @ExceptionHandler.timeout_handler
    def until(cls,method,argv = None,parent = None):
        method = method.replace('_',' ')
        if parent is not None:
            ele_proxy = ElementProxy(cls.waiter.until(lambda d:parent.find_element( *(method,argv) )))
            return ele_proxy
        else:
            ele_proxy = ElementProxy(cls.waiter.until( EC.presence_of_element_located( (method,argv) ) ))
            return ele_proxy


    @classmethod
    def class_name(cls,cls_name,**kwargs):
        parent_ele = kwargs.get('parent',None)
        return cls.until(cls.class_name.__name__,cls_name,parent_ele)

    @classmethod
    def id(cls,id,**kwargs):
        parent_ele = kwargs.get('parent',None)
        return cls.until(cls.id.__name__,id,parent_ele)

    @classmethod
    def tag_name(cls,tag,**kwargs):
        parent_ele = kwargs.get('parent',None)
        return cls.until(cls.tag_name.__name__,tag,parent_ele)

    @classmethod
    def link_text(cls,tag,**kwargs):
        parent_ele = kwargs.get('parent',None)
        return cls.until(cls.link_text.__name__,tag,parent_ele)

    @classmethod
    def xpath(cls,xpath,**kwargs):
        parent_ele = kwargs.get('parent',None)
        return cls.until(cls.xpath.__name__,xpath,parent_ele)

class FindElementsBy(object):
    """
        class comment
    """
    drv = FindElementBy.drv
    waiter = FindElementBy.waiter

    def __init__(self):
        pass

    @classmethod
    @ExceptionHandler.timeout_handler
    def until(cls,method,argv,parent = None):
        method = method.replace('_',' ')
        if parent is not None:
            return (ElementProxy(e) for e in cls.waiter.until( lambda d:parent.find_elements( *(method,argv) ) ))
        else:
            return (ElementProxy(e) for e in cls.waiter.until( EC.presence_of_all_elements_located( (method,argv) ) ))

    @staticmethod
    def muti_elements(func):
        def _muti_elements(obj): #--obj is a *PageElements class
            #print 'muti_elements',func,obj
            return func(obj)
        return _muti_elements

    @classmethod
    def class_name(cls,cls_name,**kwargs):
        parent_ele = kwargs.get('parent',None)
        return cls.until(cls.class_name.__name__,cls_name,parent_ele)


class WaitFor(object):
    """
        class comment
        WwaitFor.title_is('Apps')
    """
    drv = Driver.get_driver()
    waiter = WebDriverWait(drv, TIME_OUT)

    @classmethod
    @ExceptionHandler.timeout_handler
    def title_is(cls,title,callback = None,*args,**kwargs):
        if cls.waiter.until(EC.title_is(title)):
            return True

    @classmethod
    @ExceptionHandler.timeout_handler
    def staleness_of(cls,ele,callback = None,*args,**kwargs):
        if cls.waiter.until(EC.staleness_of(ele)):
            return True

    @classmethod
    @ExceptionHandler.timeout_handler
    def until(cls,f,callback = None,*args,**kwargs):
        if cls.waiter.until(lambda d:f()):
            return True

class ElementProxy(object):
    def __init__(self,web_ele):
        #print web_ele,WebElement,type(web_ele),type(WebElement)
        if isinstance(web_ele,WebElement):
            self.ele = web_ele
        else:
            raise TypeError,'%s is not a WebElement'%(web_ele)


    def __getattr__(self, name):
        return getattr(self.ele,name)



    def click(self,ignore_mask = False):
        ele = self.ele
        if FindElementBy.waiter.until(lambda d: ele.is_displayed() and ele.is_enabled()):
            if not ignore_mask:
                cover = FindElementBy.id('table-mask')
                if cover:
                    if not WaitFor.until(lambda:cover.value_of_css_property('display') == 'none'):
                        print 'element can not clickable,location is ',ele.location
                        raise TimeoutException, 'element can not clickable,location is ',ele.location
            ele.click()
            return True
        raise TimeoutException, 'element can not clickable,location is ',ele.location


class BaseElement(object):
    """
        class comment
    """

    def __init__(self):
        pass

    def __getattribute__(self, *args,**kwargs):
        method, = args
        #print method,args,kwargs
        locator = object.__getattribute__(self,method)()
        if object.__getattribute__(self,method).__name__ != method:
            #print 'return elements list '
            return lambda: FindElementsBy.until(*locator)
        else:
            return lambda: FindElementBy.until(*locator)


class BasePage(object):
    """
        class comment
    """

    bind_pattern = None

    def __init__(self,*args,**kwargs):
        self.driver = Driver.get_driver()
        self.bind_win(self.bind_pattern)



    def can_clickable(self,ele,ignore_mask = False):
        #return FindElementBy.waiter.until(lambda d: ele.is_displayed() and ele.is_enabled())
        if FindElementBy.waiter.until(lambda d: ele.is_displayed() and ele.is_enabled()):
            if not ignore_mask:
                cover = FindElementBy.id('table-mask')
                if not WaitFor.until(lambda:cover.value_of_css_property('display') == 'none'):
                    return False
            return True
        return False


    def close(self):
        self.wmgr.unbind(self,self.driver.current_window_handle)

    @staticmethod
    def close_when_done(func):
        @wraps(func)
        def _close_when_done(obj,*args,**kwargs):
            ret = func(obj,*args,**kwargs)
            WindowsManager().unbind(obj,Driver.get_driver().current_window_handle)
            return ret
        return _close_when_done

    def bind_win(self,bind_pattern = None):
        self.wmgr = WindowsManager()
        if bind_pattern is None:
            self.wmgr.bind(self.driver.current_window_handle,self)
            return True
        driver = Driver
        now_handle = self.driver.current_window_handle
        print('before bind:',driver.get_all_wins())
        for hwd in driver.get_all_wins():
            #if hwd != now_handle:
            driver.switch_win(hwd)
            print hwd,driver.get_title(),driver.get_cur_url() #--2涓殑瓒呮椂杩樿澶勭悊鍟?
            if re.findall(bind_pattern,driver.get_cur_url()):  #--杩欓噷搴旇澶勭悊杞爜鐨勶紝浣嗗厛涓嶈浆浜?
                self.wmgr.bind(hwd,self)
                print('binded:',hwd,driver.get_cur_url())
                return True
        driver.switch_win(now_handle)
        return False


class MainPageElements(BaseElement):
    """
        class comment
    """

    def login_btn(self):
        return (By.CLASS_NAME,'login_btn')


    def test_btn(self):
        return (By.ID,'not_exist_btn')

class MainPage(BasePage):

    def __init__(self,url):
        super(MainPage,self).__init__()
        self.url = url
        self.driver.get(url)

    @BasePage.close_when_done
    def click_login_btn(self):
        btn = MainPageElements().login_btn()
        btn.click(True)
        #if self.can_clickable(btn,True):
        #    btn.click()
        #self.driver.find_element(*self.login_btn).click()

class LoginPageElements(BaseElement):
    """
        class comment
    """

    def __init__(self):
        pass

    def user_input(self):
        return ('name','u')

    def pwd_input(self):
        return ('name','p')

    def login_btn(self):
        return (By.ID,'login_button')

    def login_type(self):
        #return (By.LINK_TEXT,u'帐号密码登录')
        #return (By.ID,'switcher_plogin')
        return (By.XPATH,'//*[@id="switcher_plogin"]')

    def frame(self):
        return (By.XPATH,'//*[@id="login_frame"]')



class LoginPage(BasePage):
    """
        class comment
    """
    page_elements = LoginPageElements()
    login_frame = 'login_frame'

    def click_btn(self,ele):
        ele.click(True)
##        if self.can_clickable(ele,True):
##            ele.click()


    def login(self,user,pwd):
        #def pp():print('login from is noe')
        #WaitFor.until(lambda:self.page_elements.login_from() != None,callback = pp )
        print('seitch frame:',self.login_frame)
        self.page_elements.frame()
        self.driver.switch_to_frame(self.login_frame)
        login_type = self.page_elements.login_type()
        login_type.click(True)
        user_input = self.page_elements.user_input()
        if self.can_clickable(user_input,True):
            user_input.clear()
            user_input.send_keys(user)
        pwd_input = self.page_elements.pwd_input()
        if self.can_clickable(pwd_input,True):
            pwd_input.clear()
            pwd_input.send_keys(pwd)
        self.click_btn(self.page_elements.login_btn())
        self.driver.switch_to_default_content()


class AppsPageElements(BaseElement):
    """
        class comment
    """
    app_name = ''

    def __init__(self):
        pass

    def link_of_app(self):
        #find_element_by_link_text(u'上报次数').click()
        return ('link_text',AppsPageElements.app_name)

    @FindElementsBy.muti_elements
    def apps_name(self):
        return (By.XPATH,'//*[@id="root"]//tbody/tr[*]/td[1]/div/div[2]')


    def old_version(self):
        return ('link_text',u'返回旧版')



class AppsPage(BasePage):
    """
        class comment
    """
    TITLE = 'APPS'
    TITLE_V2 = u'我的产品-Bugly'
    page_elements = AppsPageElements()

    @BasePage.close_when_done
    def select_app(self,app_name):
        AppsPageElements.app_name = app_name
        self.page_elements.link_of_app().click(True)
        return
        '''
        #--v2
        self.wait_for_ele_refresh()
        for ele in self.page_elements.apps_name():
            if ele.text == AppsPageElements.app_name:
                print('select app ok~~')
                ele.click(True)
                return
        '''

    def wait_for_ele_refresh(self):
        #--等一个元素过期，true表示页面已经刷新�?
        ele_list = self.page_elements.apps_name()
        for e in ele_list:
            if WaitFor.staleness_of(e):
                print 'refresh ok,get new webElement'
                return

    def redirect_v1(self):
        self.page_elements.old_version().click()




class IssuesPageElements(BaseElement):
    """
        class comment
    """

    def __init__(self):
        pass


    def issue_active(self):
        return ('link_text',u'崩溃')


    def ver_select(self):
        return ('name','version')

    def ver_input(self):
        #--.find_element_by_xpath('//*[@id="version_select_chosen"]/div/div/input')
        return ('xpath','//*[@id="version_select_chosen"]/div/div/input')

    def crash_input(self):
        #--.find_element_by_xpath('//*[@id="version_select_chosen"]/div/div/input')
        return ('xpath','//*[@id="type_select_chosen"]/div/div/input')

    def page_num(self):
        return ('id','all-page')

    def crash_count(self):
        return ('link_text',u'上报次数')

    def tbody(self):
        #--tbody = self.driver.find_element_by_xpath('//*[@id="issue-table"]/tbody')
        return ('xpath','//*[@id="issue-table"]/tbody')

    @FindElementsBy.muti_elements
    def refresh_tr_list(self):
        #--#--//*[@id="issue-table"]/tbody/tr[3]/td[2]/h1/a
        return ('class_name','refresh')

    def count_input(self):
        return ('id','count-input')

    def next_btn(self):
        return ('id','next')

    def last_uptime(self):
        return (By.XPATH,'//*[@id="issue-table"]/tbody/tr[3]/td[4]')

    @FindElementsBy.muti_elements
    def bug_users(self):
        return (By.CLASS_NAME,'usrs')

    def app_id(self):
        return (By.XPATH,'//*[@id="apps-select"]/div[1]/span')

class IssuesPage(BasePage):
    """
        class comment
    """
    page_elements = IssuesPageElements()
    total_page = 0
    bug_data = load()
    bug_state = False
    total_bug_users = 0
    app_id = 0

    def select_version(self,ver):
        active_main = self.page_elements.issue_active()
        active_main.click()
        if not WaitFor.staleness_of(active_main):
            print 'active main failed,exit!!!'
        else:
            print 'active_main ok'
        print 'select version:',ver
        ele = self.page_elements.page_num()
        page_num = ele.text
        print 'page num:',page_num
        self.page_elements.ver_input().send_keys(ver)
        self.page_elements.ver_input().send_keys(Keys.ENTER)
        WaitFor.until(lambda:self.page_elements.page_num().text != page_num)
        print 'page num:',ele.text,type(ele.text)
        IssuesPage.total_page = int(ele.text)
##        if FindElementBy.waiter.until( lambda d: self.page_elements.page_num().text != page_num):
##            print 'page num:',ele.text,type(ele.text)
##            IssuesPage.total_page = int(ele.text)

    def sort_bug_by_crash_count(self):
        ele = self.page_elements.crash_count()
        ele.click()
##        if self.can_clickable(ele):
##            #print 'sorted bugs'
##            ele.click()
        self.wait_for_ele_refresh()


    def click_bug(self,tr,bid,bugs):
        #print tr,bid,bugs,need_log
        '''
        return:
        -1:not found
        1:old bug ignore
        2:bugs empty
        bid:ok
        '''
        is_getall = len(bugs) == 1 and bugs[0] == '#'
        need_log = False
        if int(bid) * 1000 + 3 in bugs:
            need_log = True
        elif bid not in bugs and not is_getall:
            return -1
        ret_bid = bid
        crash_times = int(FindElementBy.class_name('times',parent = tr).text.replace(',',''))
        if self.bug_data.get(bid,999999) <= crash_times:
            if need_log:
                rbid = int(bid) * 1000 + 3
            else:
                rbid = bid
            if not is_getall:
                bugs.remove(rbid)
            return 1
        self.bug_state = True
        G_DIFF_TIMES[bid] = crash_times - self.bug_data.get(bid,0)
        self.bug_data[bid] = crash_times
        #--临时处理一下下
        #--//*[@id="issue-table"]/tbody/tr[6]/td[2]/h1/a
        #bug_link = FindElementBy.link_text('Exception',parent = tr)
        bug_link = FindElementBy.xpath('td[2]/h1/a',parent = tr)
        #--临时处理一下下end
        bug_link.click()
        ##                if self.can_clickable(bug_link):
        ##                    bug_link.click()
        if need_log:
            bid = bid * 1000 + 3
        if not is_getall:
            bugs.remove(bid)
        if not bugs:
            return 2
        return ret_bid

    def select_bug(self,bugs,has_sorted = False ):
        #print bugs
        for tr in self.page_elements.refresh_tr_list():
            #--//*[@id="issue-table"]/tbody/tr[3]/td[2]/h1/a
            bid = int(FindElementBy.tag_name('label',parent = tr).text)
            #bid = int(tr.find_element_by_tag_name('label').text)
            print bid
            if bid in bugs:
                ret = self.click_bug(tr,bid,bugs)
                #print 'ret1:',ret
                if ret == 1:
                    continue
                elif ret == 2:
                    break
            elif int(bid) * 1000 + 3 in bugs:
                ret = self.click_bug(tr,bid,bugs,True)
                #print 'ret2:',ret
                if ret == 1:
                    continue
                elif ret == 2:
                    break
        #--TODO:
        #--已经全部找完了，但还会继续找的问�?
        if bugs:
            #--没找完，可能需要翻页去�?
            #--is last page?
            cur_page = int(self.page_elements.count_input().get_attribute('value'))
            print 'cur page:',cur_page
            if cur_page >= self.total_page:
                print 'no bug found!!!'
                return
            self.next_issues_page()
            self.select_bug(bugs,True)
        save(self.bug_data)
        return self.bug_state

    def select_bug_ex(self,bugs,has_sorted = False ):
        while True:
            for tr in self.page_elements.refresh_tr_list():
                bid = int(FindElementBy.tag_name('label',parent = tr).text)
                print bid
                ret = self.click_bug(tr,bid,bugs)
                if ret == 1:
                    continue
                elif ret == 2:
                    yield ret
                    break
                elif ret == -1:
                    pass
                else:
                    yield ret

            #--TODO:
            #--已经全部找完了，但还会继续找的问�?
            if bugs:
                #--没找完，可能需要翻页去�?
                #--is last page?
                cur_page = int(self.page_elements.count_input().get_attribute('value'))
                print 'cur page:',cur_page
                if cur_page >= self.total_page:
                    print 'no bug found!!!'
                    break
                self.next_issues_page()
                self.select_bug_ex(bugs,True)
            else:
                print u'没有需要再抓取的更新。如需要重新抓取，请删掉db.dat'
                break
        save(self.bug_data)



    def next_issues_page(self):
        btn = self.page_elements.next_btn()
        btn.click()
##        if self.can_clickable(btn):
##            btn.click()
        self.wait_for_ele_refresh()



    def wait_for_ele_refresh(self):
        #--等一个元素过期，true表示页面已经刷新�?
        ele_list = self.page_elements.refresh_tr_list()
        for e in ele_list:
            if WaitFor.staleness_of(e):
                print 'refresh ok,get new webElement'
                return

    def get_total_bug_users(self):
        #--可能也会有递归深度的问题，后面改吧
        for user in self.page_elements.bug_users():
            num = to_number(user.text)
            print user,num
            self.total_bug_users += num
        cur_page = int(self.page_elements.count_input().get_attribute('value'))
        print 'get_total_bug_users cur page:',cur_page
        total_page = to_number(self.page_elements.page_num().text)
        if cur_page >= total_page:
            return self.total_bug_users
        self.next_issues_page()
        self.get_total_bug_users()

        return self.total_bug_users

    def select_crash_type(self,crash_type):
        active_main = self.page_elements.issue_active()
        active_main.click()  #--这里刷新过快了，容易出现问题，再
        if not WaitFor.staleness_of(active_main):
            print 'active main failed,exit!!!'
        else:
            print 'active_main ok'

        print 'select crash_type:',crash_type
        #ele = self.page_elements.page_num().text
        print('self.page_elements.page_num():', self.page_elements.page_num())
        page_num = self.page_elements.page_num().text
        print('page_num type:',type(page_num))
        print('self.page_elements.page_num().text != u -->',self.page_elements.page_num().text != u'')
        WaitFor.until(lambda:self.page_elements.page_num().text != u'')
        print 'select_crash_type page num:',page_num
        self.page_elements.crash_input().send_keys(crash_type)
        self.page_elements.crash_input().send_keys(Keys.ENTER)
        if WaitFor.until(lambda:self.page_elements.page_num().text != page_num):
            print('self.page_elements.page_num().text != page_num')
        else:
            pritn('self.page_elements.page_num().text === page_num')
        print 'select_crash_type1 page num:',self.page_elements.page_num().text
        IssuesPage.total_page = int(self.page_elements.page_num().text)


        #self.wait_for_ele_refresh()

    def get_all_crash_devices(self,crash_type):
        target_url = 'http://bugly.qq.com/exce/issueInsideStat'
        if not self.app_id:
            self.app_id = re.findall('(\d+)',self.page_elements.app_id().text)[0]
            print 'APPID is ',self.app_id
        self.select_crash_type(crash_type)
        total_page = to_number(self.page_elements.page_num().text)
        print 'total_page-----',total_page
        while(True):
            for tr in self.page_elements.refresh_tr_list():
                bid = int(FindElementBy.tag_name('label',parent = tr).text)
                print bid
                payload = {'app': self.app_id, 'pid': 1,'ii' : bid}
                cookies = {}
                for s in Driver.get_driver().get_cookies():
                    cookies[s['name']] = s['value']
                req_obj = None
                for i in xrange(3):
                    try:
                        req_obj = requests.get(target_url,params = payload,cookies = cookies)
                        if int(req_obj.status_code) == 200:
                            break
                    except:
                        continue
                if req_obj:
                    yield req_obj.json()
            cur_page = int(self.page_elements.count_input().get_attribute('value'))
            print 'cur page:',cur_page
            if cur_page >= total_page:
                print 'no bug found!!!'
                break
            self.next_issues_page()

class IssuesDetailPageElement(BaseElement):
    """
        class comment
    """


    def more(self):
        return (By.LINK_TEXT,u'查看更多')
        #return (By.XPATH,'/html/body/div[3]/div[2]/div[4]/div[1]/p/a')

    def issue_active(self):
        return ('link_text',u'崩溃')


class IssuesDetailPage(BasePage):
    """
        class comment
    """

    page_elements = IssuesDetailPageElement()
    TITLE = u'issue详情'
    bind_pattern = 'pid=\d+&ii=\d+.*' #pid=1&ii=2181#stack

    #@BasePage.close_when_done
    def click_more_crash(self):
        active_main = self.page_elements.issue_active()
        ele = self.page_elements.more()
        if ele:
            ele.click(True)
        else:
            print('###reload page')
            Driver.reload()
            if not WaitFor.staleness_of(active_main):
                print 'active main failed,exit!!!'
            else:
                print 'reload ok'
            print(Driver.get_cur_url())
            print(Driver.get_all_wins())
            ele = self.page_elements.more()
            WaitFor.until(lambda:ele is not None)
            ele.click(True)
##        if self.can_clickable(ele,True):
##            ele.click()

class CrashDetailPageElement(BaseElement):
    """
        class comment
    """

    def __init__(self):
        pass


    #@FindElementsBy.muti_elements
    def stack_table(self):
        return ('id','stack-table')

    def stack_info(self):
        return ('id','stack-info')

    @FindElementsBy.muti_elements
    def excetion_info(self):
        #--#--//*[@id="stack-table"]/tbody/tr/th/p/text()  //*[@id="stack-info"]/div/p/text() //*[@id="stack-info"]/div/p
        #--第一次打开和再点击一次的xpath不一样的···蛋疼
        return ('xpath','//*[@id="stack-info"]/div/p')

    @FindElementsBy.muti_elements
    def other_exception(self):
        return (By.CLASS_NAME,'col-1')

    @FindElementsBy.muti_elements
    def crash_list(self):
        return ('xpath','//*[@id="crash-nav"]/li')#--//*[@id="crash-nav"]/li[1]

    def next_page(self):
        return ('id','next-page')

    def crash_pages(self):
        return ('id','crash-pages')

    def cur_page(self):
        return ('id','cur-page')

    def crash_title(self):
        return ('id','crash-title')

    def crash_id(self):
        return ('class_name','no')

    def sys_log(self):
        return (By.LINK_TEXT,u'系统日志')

    def log_level(self):
        return (By.XPATH,'//*[@id="log-info"]/form/div[1]/select/option[5]')

    @FindElementsBy.muti_elements
    def err_logs(self):
        return (By.CLASS_NAME,'errorLog')

class CrashDetailPage(BasePage):
    """
        class comment
    """
    page_element = CrashDetailPageElement()
    db = pickledb.load('info.db', False)

    def __init__(self):
        super(CrashDetailPage,self).__init__()
        self.data_source = DataSet()
        self.crash_count = 0
        self.bug_id = int(self.page_element.crash_id().text.replace('#',''))
        print '--------------------->',self.bug_id


    def has_exist_excetion(self,text):
        m = hashlib.md5()
        m.update(text)
        md5 = m.hexdigest()
        try:
            if self.db.dexists(md5,'exception'):
                return True,md5
            else:
                #--不会走到这里，因为不存在肯定会报错
                pass
        except KeyError:
            self.db.dcreate(md5)
            return False,md5
        '''
        self.excetion_map = getattr(self,'excetion_map',{})
        m = hashlib.md5()
        m.update(text)
        md5 = m.hexdigest()
        if self.excetion_map.has_key(md5):
            return True
        self.excetion_map[md5] = True
        return False
        #'''


    def get_excetion_info(self,bug_id,is_get_all = False, page_limit = 20):
        '''
        TODO:要改成不要用递归的形式，联运的时候页数太多了，突破了996的盏限制
        '''
        print('is_get_all:',is_get_all)
        while(1):
            need_log = False
            if (bug_id - 3) / 1000 == self.bug_id:
                need_log = True
            crash_list = self.page_element.crash_list()
            for crash_item in crash_list:
                if self.crash_count > G_DIFF_TIMES[self.bug_id]:
                    print 'old bug ignore,finish>>>>>>>>>>>',self.crash_count,G_DIFF_TIMES[self.bug_id],self.bug_id
                    return
                self.crash_count+=1
                #if self.can_clickable(crash_item):
                if crash_item.click():
                    WaitFor.staleness_of(self.page_element.crash_title())
                    lines = self.page_element.excetion_info()
                    lines = FindElementsBy.class_name('break-all',parent = self.page_element.stack_info())
                    completed_lines = ''.encode("utf-8")
                    for l in lines:
                        l.text = l.text.encode("utf-8")
                        result, number = re.subn('0x\w+','', l.text) #--主要是过滤掉内存地址，这样雷同的就少很多了
                        completed_lines += result
                    has_exist,md5 = self.has_exist_excetion(completed_lines)
                    if not has_exist:
                        Log.save(completed_lines)
                        Log.save('-' * 80)
                        print completed_lines
                        print '-' * 50
                        self.data_source.add(l.text)
                        self.db.dadd(md5,('exception',completed_lines)) #--记录出现次数
                        self.db.dadd(md5,('times',1)) #--记录出现次数
                        self.db.dadd(md5,('update',time.time())) #-- 记录的时间戳
                    else:
                        times = int(self.db.dget(md5,'times'))
                        self.db.dadd(md5,('times',times + 1)) #--记录出现次数
                        self.db.dadd(md5,('update',time.time())) #-- 记录的时间戳

                    #--save
                    self.db.dump()

                    #--抓系统log
                    if need_log:
                        self.get_sys_log()
                    if is_get_all:
                        #--坦克之外的就只取一次就够了，因为其他都是相同的
                        break
            if is_get_all:
                break

            if int(self.page_element.crash_pages().text.replace(',','')) > int(self.page_element.cur_page().get_attribute('value').replace(',','')):
                self.next_page()
                WaitFor.staleness_of(self.page_element.stack_table())
                yield True
            else:
                break



    def get_sys_log(self):
        return
        err_logs = self.page_element.err_logs()
        if err_logs is not None:
            for l in err_logs:
                if l is not None and l != '':
                    Log.save('sys log:')
                    Log.save(l.text)
                    Log.save('-' * 80)
                    print l.text
                    print '-' * 80


    def next_page(self):
        self.page_element.next_page().click()


    def close(self):
        super(CrashDetailPage,self).close()
        #Log.close()
        self.data_source.commit()


class UserDataPageElements(BaseElement):
    """
        class comment
    """

    def __init__(self):
        pass

    def userdata_active(self):
        return (By.LINK_TEXT,u'运营')

    def ver_input(self):
        #--.find_element_by_xpath('//*[@id="version_select_chosen"]/div/div/input')
        return ('xpath','//*[@id="version_select_chosen"]/div/div/input')

    def last_day(self):
        return (By.XPATH,'/html/body/div[3]/div[2]/div[3]/table[1]/tbody/tr[last()]/td[1]')

    def user_num(self):
        return (By.XPATH,'/html/body/div[3]/div[2]/div[3]/table[1]/tbody/tr[last()]/td[5]')

    def useless(self):
        return (By.CLASS_NAME,'mod-table-wrap')

class UserDataPage(BasePage):
    page_elements = UserDataPageElements()
    bind_pattern = 'ver=[0-9a-zA-Z_.]+&time=[0-9a-zA-Z_.]+'
    #ver=1.3.1.3&time=last_7_day
    TITLE = u'运营'

    def __init__(self,ver):
        super(UserDataPage,self).__init__()
        self.page_elements.userdata_active().click()
        if WaitFor.title_is(self.TITLE):
            print 'UserDataPage init ok,just take a while'
        else:
            print 'UserDataPage failed'
        self.page_elements.ver_input().send_keys(ver)
        self.page_elements.ver_input().send_keys(Keys.ENTER)
        ele = self.page_elements.useless()
        WaitFor.staleness_of(ele)

    def get_user_num(self):
        last_day = self.page_elements.last_day().text
        user_num = self.page_elements.user_num().text
        print 'date is %s,get user num is %s'%(last_day,user_num)
        return to_number(user_num)


class Player(object):
    """
        class comment
    """

    def __init__(self,op_obj):
        self.options = op_obj
        self.reg_option_handler()

    @staticmethod
    def after_action(func):
        @wraps(func)
        def _after_action(obj,*args,**kwargs):
            Driver.get_driver().close()
            sys.exit()
        return _after_action

    def reg_option_handler(self):
        self.fun_map = {}
        self.fun_map[self.options.ANALYSIS] = self.action_analysis
        self.fun_map[self.options.GET_ALL] = self.action_getall
        self.fun_map[self.options.FREE_GET] = self.action_freeget
        self.fun_map[self.options.CRASH_DEVICES] = self.action_crash_devices


    #@Player.after_action
    def do_action(self,*args,**kwargs):
        print 'action %s start!!!'%(self.options.get_type())
        self.fun_map[self.options.get_type()](*args,**kwargs)
        print 'action %s end!!!'%(self.options.get_type())
        Driver.get_driver().close()
        sys.exit()


    def action_analysis(self,*args,**kwargs):
        page_obj, = args
        pi = page_obj
        total_crash_users = 0
        #pi.select_crash_type(u'Java Crash')
        pi.select_crash_type(u'Native Crash')
        native_bug_users = pi.get_total_bug_users()
        total_crash_users = native_bug_users
        print('native_bug_users:',native_bug_users)
        #pi.select_crash_type(u'Native Crash')
        pi.select_crash_type(u'Java Crash')
        java_bug_users = pi.get_total_bug_users()
        total_crash_users += java_bug_users
        pi.close()
        print(u'总影响用户数为{0}，其中Java层闪退有{1}，占比{2}；Native层闪退有{3}，占比{4}'.format(total_crash_users,java_bug_users,java_bug_users / total_crash_users,native_bug_users,native_bug_users / total_crash_users))
        '''
        新版bugly有出错率了，老的出错率分析就不要了
        print 'bug_users:',bug_users
        pu = UserDataPage(self.options.version)
        user_num = pu.get_user_num()
        pu.close()
        print 'user_num:',user_num
        print u'目前受影响用户%s,昨日使用用户%s'%(bug_users,user_num)
        print u'昨日影响率大约是',(float(bug_users)/float(user_num))*100,'%'
        '''


    def action_freeget(self,*args,**kwargs):
        page_obj, = args
        pi = page_obj
        bugs_list = self.options.bugs_list
        if self.options.get_type() == self.options.GET_ALL:
            bugs_list = ['#',]
        else:
            bugs_list = map(lambda t: int(t),bugs_list)  #--1531
        print 'bugs_list-------------->',bugs_list
        for bug_id in pi.select_bug_ex(bugs_list):
            if not bug_id:
                #--code never reach here
                print u'没有需要抓取的更新。如需要重新抓取，请删掉db.dat'
                #Driver.get_driver().close()
                #sys.exit()
                break
            pid = IssuesDetailPage()
            time.sleep(2) #--妈的，强制2秒，不写逻辑了
            pid.click_more_crash()
            WaitFor.title_is(u'Crash列表') #--看标题也不是很严谨，如果网速慢点的话也很容易出问题
            pcd = CrashDetailPage()
            for state in pcd.get_excetion_info(bug_id,self.options.get_type() == self.options.GET_ALL):
                if state:
                    continue
                break
            #pcd.get_excetion_info(bug_id)
            pid.close()
            pcd.close()
        pi.close()

    def action_getall(self,*args,**kwargs):
        self.action_freeget(*args,**kwargs)

    def action_crash_devices(self,*args,**kwargs):
        page_obj, = args
        pi = page_obj
        devices_map = {} #--model : crash_times
        for crash_type in ('Java Crash','Native Crash'):
            for data in pi.get_all_crash_devices(crash_type):
                #print '---',data['modelStatData']
                struct = json.loads(data['modelStatData'])
                for item in struct:
                    if not re.findall('[\d\w]',item['name']):
                        continue
                    print lazy_pinyin(item['name'])
                    model = ''.join( lazy_pinyin(item['name']) )
                    times = item['value']
                    print 'model:',model
                    devices_map[model] = (getattr(devices_map,model,0) or 0)  + int(times)
        for d,t in sorted(devices_map.iteritems(),key = lambda d:d[1],reverse = True):
            print "%s---->%s"%(d,t)
            Tools.save(d,str(t))
        Tools.close()





if __name__ == '__main__':
    os.environ['path'] = ';'.join([os.environ['path'],os.getcwd()])
##    options.version = '1.3.1.8'
##    options.analysis = False
##    options.appname = u'坦克霸主'
##    #options.bugs_list = [8114,]
##    options.type = options.GET_ALL
    url = 'http://bugly.qq.com/'
    player = Player(options)
    u = account.user
    p = account.pwd
    MainPage(url).click_login_btn()
    pl = LoginPage()
    pl.login(u,p)
    WaitFor.title_is(AppsPage.TITLE_V2,lambda : pl.page_elements.login_btn().click())
    pl.close()
    pa = AppsPage()
    pa.redirect_v1()
    WaitFor.title_is(pa.TITLE)
    pa.select_app(options.appname) #u'坦克霸主'
    pi = IssuesPage()
    try:
        ver = unicode( options.version,'utf8')
    except:
        ver = unicode( options.version,'gbk')
    pi.select_version(ver)
    pi.select_crash_type(u'Unity C# Exception')
    pi.sort_bug_by_crash_count()
    player.do_action(pi)

